{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 实战练习之一 AI股票拟合算法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print?\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "\n",
    "import tensorflow as tf\n",
    "import pandas            as pd\n",
    "import tensorflow        as tf  \n",
    "import numpy             as np\n",
    "import matplotlib.pyplot as plt\n",
    "# 支持中文\n",
    "plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签\n",
    "plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号\n",
    "\n",
    "from numpy                 import array\n",
    "from sklearn               import metrics\n",
    "from sklearn.preprocessing import MinMaxScaler\n",
    "from tensorflow.keras.models          import Sequential\n",
    "from tensorflow.keras.layers          import Dense,LSTM,Bidirectional\n",
    "\n",
    "from numpy.random   import seed\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.__path__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 确保结果尽可能重现\n",
    "\n",
    "seed(1)\n",
    "tf.random.set_seed(1)\n",
    "\n",
    "# 设置相关参数\n",
    "n_timestamp  = 5    # 时间戳\n",
    "n_epochs     = 30    # 训练轮数\n",
    "# ====================================\n",
    "#      选择模型：\n",
    "#            1: 单层 LSTM\n",
    "#            2: 多层 LSTM\n",
    "#            3: 双向 LSTM\n",
    "# ====================================\n",
    "model_type = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入tushare\n",
    "import tushare as ts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#pro=ts.get_hist_data('603722')\n",
    "help(ts.pro_api)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# 初始化pro接口\n",
    "pro = ts.pro_api('66f88ebe33ca20ebed66ba99509f7607b6a05f1e64f51454e3d4021c')\n",
    "\n",
    "# 拉取数据\n",
    "data = pro.daily(**{\n",
    "    \"ts_code\": '603722.SH',\n",
    "    \"trade_date\": \"\",\n",
    "    \"start_date\": \"2017-10-1\",\n",
    "    \"end_date\": \"\",\n",
    "    \"offset\": \"\",\n",
    "    \"limit\": \"\"\n",
    "}, fields=[\n",
    "    \"trade_date\",\n",
    "    \"open\",\n",
    "    \"close\",\n",
    "    \"high\",\n",
    "    \"low\",\n",
    "    \"vol\",\n",
    "    \"ts_code\"\n",
    "])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(data)\n",
    "data.to_csv(\"stock603722.csv\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data=pd.read_csv(\"./stock603722.csv\",parse_dates=[\"trade_date\"],index_col=\"trade_date\")[[\"open\",\"high\",\"low\",\"close\"]]\n",
    "# data=pd.read_csv(\"./stock688333.csv\",parse_dates=[\"trade_date\"])[[\"trade_date\",\"open\",\"high\",\"low\",\"close\",\"vol\"]]\n",
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mydata=pd.read_csv(\"./stock603722.csv\")[[\"open\",\"high\",\"low\",\"close\"]]\n",
    "plt.plot(mydata)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意：把pandas数据逆序，需要把数据导出到numpy再赋值，否则不改变原来数据的内容"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myindex=range(1268)\n",
    "mydata.loc[:]=mydata.iloc[::-1].to_numpy()\n",
    "mydata[:2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 获取股价数据\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "# import akshare as ak\n",
    "\n",
    "\n",
    "\n",
    "df3 = data.reset_index().iloc[-30:, :6]  # 取过去30天数据\n",
    "df3 = df3.dropna(how='any').reset_index(drop=True) #去除空值且从零开始编号索引\n",
    "df3 = df3.sort_values(by='trade_date', ascending=True)\n",
    "print(df3.info())\n",
    "\n",
    "# 均线数据\n",
    "df3['5'] = df3.close.rolling(5).mean()\n",
    "df3['10'] = df3.close.rolling(10).mean()\n",
    "\n",
    "df3.tail()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib\n",
    "matplotlib.style.use('ggplot') #用于调整图标样式，可选\n",
    "import matplotlib.pyplot as plt\n",
    "from mplfinance.original_flavor import candlestick2_ohlc\n",
    "from matplotlib.ticker import FormatStrFormatter\n",
    "\n",
    "fig, ax = plt.subplots(1, 1, figsize=(8,3), dpi=200)\n",
    "\n",
    "candlestick2_ohlc(ax,\n",
    "                  opens = df3[ 'open'].values,\n",
    "                highs = df3['high'].values,\n",
    "                lows = df3[ 'low'].values,\n",
    "                closes = df3['close'].values,\n",
    "                  width=0.5, colorup=\"r\",colordown=\"g\")\n",
    "\n",
    "# 显示最高点和最低点\n",
    "ax.text( df3.high.idxmax(), df3.high.max(),   s =df3.high.max(), fontsize=8)\n",
    "ax.text( df3.high.idxmin(), df3.high.min()-2, s = df3.high.min(), fontsize=8)\n",
    "\n",
    "ax.set_facecolor(\"white\")\n",
    "ax.set_title(\"阿科力\")\n",
    "\n",
    "# 画均线\n",
    "plt.plot(df3['5'].values, alpha = 0.5, label='MA5')\n",
    "plt.plot(df3['10'].values, alpha = 0.5, label='MA10')\n",
    "\n",
    "ax.legend(facecolor='white', edgecolor='white', fontsize=6)\n",
    "\n",
    "# 修改x轴坐标\n",
    "plt.xticks(ticks =  np.arange(0,len(df3)), labels = df3.trade_date.dt.strftime('%Y-%m-%d').to_numpy() )\n",
    "plt.xticks(rotation=90, size=8)\n",
    "\n",
    "# 修改y轴坐标\n",
    "ax.yaxis.set_major_formatter(FormatStrFormatter('%.2f'))\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#from matplotlib.dates  import date2num\n",
    "#data['date']=date2num(data.index.to_pydatetime())\n",
    "#data=data.sort_values(by=\"date\",ascending=True)\n",
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 获取股价数据\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "df3 = data.reset_index().iloc[:,:]  #取过去30天数据\n",
    "df3 = df3.dropna(how='any').reset_index(drop=True) #去除空值且从零开始编号索引\n",
    "print(df3)\n",
    "df3 = df3.sort_values(by='trade_date', ascending=True)\n",
    "print(df3.info())\n",
    "\n",
    "# 均线数据\n",
    "df3['5'] = df3.close.rolling(5).mean()\n",
    "df3['10'] = df3.close.rolling(10).mean()\n",
    "df3['30']=df3.close.rolling(30).mean()\n",
    "\n",
    "df3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib\n",
    "matplotlib.style.use('ggplot') #用于调整图标样式，可选\n",
    "import matplotlib.pyplot as plt\n",
    "from mplfinance.original_flavor import candlestick2_ohlc\n",
    "from matplotlib.ticker import FormatStrFormatter\n",
    "\n",
    "fig, ax = plt.subplots(1, 1, figsize=(8,3), dpi=200)\n",
    "\n",
    "candlestick2_ohlc(ax,\n",
    "                  opens = df3[ 'open'].values,\n",
    "                highs = df3['high'].values,\n",
    "                lows = df3[ 'low'].values,\n",
    "                closes = df3['close'].values,\n",
    "                  width=0.5, colorup=\"r\",colordown=\"g\")\n",
    "\n",
    "# 显示最高点和最低点\n",
    "ax.text( df3.high.idxmax(), df3.high.max(),   s =df3.high.max(), fontsize=8)\n",
    "ax.text( df3.high.idxmin(), df3.high.min()-2, s = df3.high.min(), fontsize=8)\n",
    "\n",
    "ax.set_facecolor(\"white\")\n",
    "ax.set_title(\"阿科力\")\n",
    "\n",
    "# 画均线\n",
    "plt.plot(df3['5'].values, alpha = 0.5, label='MA5')\n",
    "plt.plot(df3['10'].values, alpha = 0.5, label='MA10')\n",
    "plt.plot(df3['30'].values,alpha=0.5, label='MA30')\n",
    "\n",
    "ax.legend(facecolor='white', edgecolor='white', fontsize=6)\n",
    "\n",
    "# 修改x轴坐标\n",
    "tempXticks=np.arange(0,len(df3))\n",
    "#XticksData=data.asfreq(\"2M\").dropna()\n",
    "nameXticks =  df3.trade_date.dt.strftime('%Y-%m-%d').to_numpy()\n",
    "\n",
    "plt.xticks(ticks =tempXticks , labels =nameXticks )\n",
    "plt.xticks(rotation=45, size=8)\n",
    "#修改X轴间隔\n",
    "x_major_locator=plt.MultipleLocator(100)\n",
    "ax.xaxis.set_major_locator(x_major_locator)\n",
    "ax.spines['bottom'].set_color('red')\n",
    "ax.spines['left'].set_color('red')\n",
    "plt.xlim(0,1100)\n",
    "# 修改y轴坐标\n",
    "ax.yaxis.set_major_formatter(FormatStrFormatter('%.2f'))\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "a=np.linspace(0,round(len(df3),0),10,dtype=int).tolist()\n",
    "b=df3.trade_date.dt.strftime('%Y-%m-%d').to_numpy()\n",
    "c=data.asfreq(\"2M\").dropna()\n",
    "#c.dropna().index\n",
    "cc=c.index.strftime('%Y-%m-%d').to_numpy()\n",
    "e=df3.trade_date.tolist()\n",
    "#f=[e[i] for i in cc]\n",
    "cc"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# data1=pd.DataFrame(data,columns=[\"trade_date\",\"close\"])\n",
    "#data1=pd.DataFrame(data,columns=[\"close\"])\n",
    "data1=data.iloc[::-1]\n",
    "data1[\"open\"].plot(label=\"open\")\n",
    "data1[\"close\"].plot(label=\"close\")\n",
    "plt.legend()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ddd=[]\n",
    "for item in data1.items():\n",
    "    ddd.append(item)\n",
    "ind=ddd[0][1].values\n",
    "da1=ddd[1][1].values\n",
    "index1=[]\n",
    "data2=[]\n",
    "for item in ind:\n",
    "    index1.append(item)\n",
    "index1.reverse()\n",
    "for item in da1:\n",
    "    data2.append(item)\n",
    "data2.reverse()\n",
    "data1={'trade_date':index1,'close':data2}\n",
    "stockdata=pd.DataFrame(data1)\n",
    "\n",
    "stockdata.items    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df3.iloc[0:3000,1:3].values[:,1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from matplotlib.dates  import date2num\n",
    "date2num(df3.trade_date.values)\n",
    "df3['date']=date2num(df3.trade_date.values)\n",
    "#df3=df3.sort_values(by=\"date\",ascending=True)\n",
    "\n",
    "#df3=df3[['date','open', 'high', 'low', 'close']]\n",
    "ind=np.arange(0,3000)\n",
    "dataf1=df3[['date','open', 'high', 'low', 'close']]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataf1.set_index(\"date\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 深度学习拟合股票"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#\n",
    "#拟合开始\n",
    "#\n",
    "training_set = dataf1.iloc[0:512, 4:5].to_numpy()#要提取一列数据，否则会出错\n",
    "test_set     = dataf1.iloc[973 - 300:, 4:5].to_numpy()\n",
    "print(training_set)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#将数据归一化，范围是0到1\n",
    "sc  = MinMaxScaler(feature_range=(0, 1))\n",
    "training_set_scaled = sc.fit_transform(training_set)\n",
    "testing_set_scaled  = sc.transform(test_set) \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 取前 n_timestamp 天的数据为 X；n_timestamp+1天数据为 Y。\n",
    "def data_split(sequence, n_timestamp):\n",
    "    X = []\n",
    "    y = []\n",
    "    for i in range(len(sequence)):\n",
    "        end_ix = i + n_timestamp\n",
    "        \n",
    "        if end_ix > len(sequence)-1:\n",
    "            break\n",
    "            \n",
    "        seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]\n",
    "        X.append(seq_x)\n",
    "        y.append(seq_y)\n",
    "    return array(X), array(y)\n",
    "\n",
    "X_train, y_train = data_split(training_set_scaled, n_timestamp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train          = X_train.reshape(X_train.shape[0], X_train.shape[1], 1)\n",
    "\n",
    "X_test, y_test   = data_split(testing_set_scaled, n_timestamp)\n",
    "X_test           = X_test.reshape(X_test.shape[0], X_test.shape[1], 1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#五、构建模型\n",
    "# 建构 LSTM模型\n",
    "model_type=3\n",
    "if model_type == 1:\n",
    "    # 单层 LSTM\n",
    "    model = Sequential()\n",
    "    model.add(LSTM(units=50, activation='relu',\n",
    "                   input_shape=(X_train.shape[1], 1)))\n",
    "    model.add(Dense(units=1))\n",
    "if model_type == 2:\n",
    "    # 多层 LSTM\n",
    "    model = Sequential()\n",
    "    model.add(LSTM(units=50, activation='relu', return_sequences=True,\n",
    "                   input_shape=(X_train.shape[1], 1)))\n",
    "    model.add(LSTM(units=50, activation='relu'))\n",
    "    model.add(Dense(1))\n",
    "if model_type == 3:\n",
    "    # 双向 LSTM\n",
    "    model = Sequential()\n",
    "    model.add(Bidirectional(LSTM(50, activation='relu'),\n",
    "                            input_shape=(X_train.shape[1], 1)))\n",
    "    model.add(Dense(1))\n",
    "\n",
    "model.summary()  # 输出模型结构"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.compile(optimizer=tf.keras.optimizers.Adam(0.001),\n",
    "              loss='mean_squared_error')  # 损失函数用均方误差\n",
    "#七、训练模型\n",
    "history = model.fit(X_train, y_train,\n",
    "                    batch_size=64,\n",
    "                    epochs=n_epochs,\n",
    "                    validation_data=(X_test, y_test),\n",
    "                    validation_freq=1)  # 测试的epoch间隔数\n",
    "\n",
    "model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.plot(history.history['loss'], label='Training Loss')\n",
    "plt.plot(history.history['val_loss'], label='Validation Loss')\n",
    "#plt.title('Training and Validation Loss')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "predicted_stock_price = model.predict(\n",
    "    X_test)                        # 测试集输入模型进行预测\n",
    "predicted_stock_price = sc.inverse_transform(\n",
    "    predicted_stock_price)  # 对预测数据还原---从（0，1）反归一化到原始范围\n",
    "real_stock_price = sc.inverse_transform(y_test)  # 对真实数据还原---从（0，1）反归一化到原始范围"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 画出真实数据和预测数据的对比曲线\n",
    "plt.plot(real_stock_price, color='red', label='Stock Price')\n",
    "plt.plot(predicted_stock_price, color='blue', label='Predicted Stock Price')\n",
    "plt.title('阿科力')\n",
    "plt.xlabel('Time')\n",
    "plt.ylabel('Stock Price')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "MSE = metrics.mean_squared_error(predicted_stock_price, real_stock_price)\n",
    "RMSE = metrics.mean_squared_error(predicted_stock_price, real_stock_price)**0.5\n",
    "MAE = metrics.mean_absolute_error(predicted_stock_price, real_stock_price)\n",
    "R2 = metrics.r2_score(predicted_stock_price, real_stock_price)\n",
    "\n",
    "print('均方误差: %.5f' % MSE)\n",
    "print('均方根误差: %.5f' % RMSE)\n",
    "print('平均绝对误差: %.5f' % MAE)\n",
    "print('R2: %.5f' % R2)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.10.4 ('python310')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.4"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "a888cd810c90be07c3c921372a6d7ff433f5a38d5779dfb2a871d99db75673fb"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
